home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 5 / Apprentice-Release5.iso / Source Code / Libraries / Dots & Pixels / sources / flowdots.cp < prev    next >
Text File  |  1995-09-29  |  9KB  |  309 lines

  1. #include <Retrace.h>
  2. #include <Devices.h>
  3. #include <SegLoad.h>
  4. #include <Timer.h>
  5.  
  6. #include <math.h>
  7. #include <iostream.h>
  8.  
  9. #include "C_randomizer.h"
  10. #include "flowsettings.h"
  11. #include "vretrace.h"
  12. #include "macutilities.h"
  13. #include "phaser.h"
  14. #include "screenarea.h"
  15. #include "screendots.h"
  16. #include "dotcollection.h"
  17. #include "flowdots.h"
  18.  
  19. flowdots::flowdots( int numbits, int xpos, int ypos,
  20.             int aantaldots, int lifetime, double how_often)
  21.     : flowparams()
  22.     , dotcollection( aantaldots)
  23.     , phaser( lifetime)
  24.     , flowsettings()
  25.     , screendots( numbits, xpos, ypos, aantaldots)
  26. {
  27.     time_of_last_apply_flow_call = 0;
  28.  
  29.     framerate = framerate_of_main_monitor();
  30.     
  31.     if( how_often > 0.0)
  32.     {
  33.         updaterate  = how_often;
  34.         auto_timing = false;
  35.     } else {
  36.         auto_timing = true;
  37.     }
  38.     compute_matrices();
  39. }
  40.  
  41. flowdots::flowdots( int numbits, screen_position where,
  42.             int aantaldots, int lifetime, double how_often)
  43.     : flowparams()
  44.     , dotcollection( aantaldots)
  45.     , phaser( lifetime)
  46.     , flowsettings()
  47.     , screendots( numbits, where, aantaldots)
  48. {
  49.     time_of_last_apply_flow_call = 0;
  50.  
  51.     framerate = framerate_of_main_monitor();
  52.     
  53.     if( how_often > 0.0)
  54.     {
  55.         updaterate  = how_often;
  56.         auto_timing = false;
  57.     } else {
  58.         auto_timing = true;
  59.     }
  60.     compute_matrices();
  61. }
  62.  
  63. void flowdots::compute_addresses()
  64. {
  65.     apply_flow();        // computes next position of all dots
  66.     //
  67.     // could use dotcollection::numdots, as well
  68.     //
  69.     #define help_the_optimizer
  70.     
  71.     #ifdef help_the_optimizer
  72.         const int loopend = screendots::numdots;
  73.         const int the_shift = coord_shift;    // aids the optimizer
  74.         unsigned char **dot_address = dot_addresses;
  75.         short *xcoord = xcoords;
  76.         short *ycoord = ycoords;
  77.         for( int i = 0; i < loopend; i++)
  78.         {
  79.             *dot_address++ =
  80.                 &screen[ *ycoord++ >> the_shift][ *xcoord++ >> the_shift];
  81.         }
  82.     #else
  83.         for( int i = 0; i < screendots::numdots; i++)
  84.         {
  85.             dot_addresses[ i] =
  86.                 &screen[ ycoords[ i] >> coord_shift][ xcoords[ i] >> coord_shift];
  87.         }
  88.     #endif
  89.     #undef help_the_optimizer
  90. }
  91.  
  92. void flowdots::apply_flow()
  93. {
  94.     (void)major_step();
  95.     if( auto_timing)
  96.     {
  97.         //
  98.         // maintain time yourself:
  99.         //
  100.         const unsigned long now_micros = readMicroseconds();
  101.         if( time_of_last_apply_flow_call == 0)
  102.         {
  103.             //
  104.             // first time around: 'smart' guess
  105.             //
  106.             updaterate = framerate / 2;
  107.         } else {
  108.             #ifdef THINK_CPLUS
  109.                 //
  110.                 // prevents a call of '_UTOX96' (SC++ 7.0 bug)
  111.                 // (941121: bug no longer really present in 7.0.4. It used to be
  112.                 // that a call to '_UTOX96' was generated, where this function
  113.                 // was not present in 'ANSI++ 881' (or any other '881 library).
  114.                 // With SC++ 7.0.4 the call to '_UTOX91' is still generated, but
  115.                 // no link errors occur, but for now, keep using a long instead
  116.                 // of an unsigned long. (this replaces a subroutine call with a
  117.                 // FPU divide. The latter is much faster, and delivers the same
  118.                 // result, as long as this code doesn't run on a Pentium ;-)
  119.                 //
  120.                 const long delay = now_micros - time_of_last_apply_flow_call;
  121.             #else
  122.                 const unsigned long delay = now_micros - time_of_last_apply_flow_call;
  123.             #endif
  124.             updaterate = 1000000.0 / (double)delay;
  125.         }
  126.         time_of_last_apply_flow_call = now_micros;
  127.         compute_matrices();
  128.     }
  129.     short *the_xcoord = xcoords;
  130.     short *the_ycoord = ycoords;
  131.     //
  132.     // Note: there must be _exactly_ one '*the_xcoord++ = …' and _exactly_ one
  133.     // '*the_ycoord++ = …' in every possible path through the loop.
  134.     //
  135.     short_long_hack the_hack;
  136.     const int loopend = dotcollection::numdots; // could also use screendots::numdots
  137.     
  138.     for( int i = 0; i < loopend; i++)    
  139.     {
  140.         if( minor_step() != 0)
  141.         {
  142.             //
  143.             // compute new position of dot
  144.             //
  145.             const long prev_x = (long) *the_xcoord;
  146.             const long prev_y = (long) *the_ycoord;
  147. //
  148. //    Note: the expression below was optimized, but now gives slightly different
  149. //    results. I haven't thought about its ramifications, yet.
  150. //    941206: The difference is thought to be too minor to be visible.
  151. //
  152. //            const long new_x = ((prev_x * x11) >> 15)
  153. //                            + ((prev_y * x12) >> 15) + trans_x + prev_x;
  154. //
  155.             const long new_x =
  156.                 (((prev_x * x11) + (prev_y * x12)) >> 15) + trans_x + prev_x;
  157.             if( new_x != (short)new_x)
  158.             {
  159.                 //
  160.                 // use one call of 'step' and split the result in two:
  161.                 //
  162.                 the_hack.ulong = randomizer_step();
  163.                 *the_xcoord++ = the_hack.shorties.left;
  164.                 *the_ycoord++ = the_hack.shorties.right;
  165.             } else {
  166. //
  167. //    Note: the expression below was optimized, but now gives slightly different
  168. //    results. I haven't thought about its ramifications, yet.
  169. //    941206: The difference is thought to be too minor to be visible.
  170. //
  171. //                const long new_y = ((prev_x * x21) >> 15)
  172. //                            + ((prev_y * x22) >> 15) + trans_y + prev_y;
  173. //
  174.                 const long new_y =
  175.                     (((prev_x * x21) + (prev_y * x22)) >> 15) + trans_y + prev_y;
  176.                 if( new_y != (short)new_y)
  177.                 {
  178.                     //
  179.                     // use one call of 'step' and split the result in two:
  180.                     //
  181.                     the_hack.ulong = randomizer_step();
  182.                     *the_xcoord++ = the_hack.shorties.left;
  183.                     *the_ycoord++ = the_hack.shorties.right;
  184.                 } else {
  185.                     *the_xcoord++ = (short)new_x;
  186.                     *the_ycoord++ = (short)new_y;
  187.                 }
  188.             }
  189.         } else {
  190.             //
  191.             // dot surpassed its lifetime
  192.             //
  193.             the_hack.ulong = randomizer_step();
  194.             *the_xcoord++ = the_hack.shorties.left;
  195.             *the_ycoord++ = the_hack.shorties.right;
  196.         }
  197.     }
  198. }
  199.  
  200. void flowdots::compute_matrices()
  201. {
  202.     //
  203.     // The translation will not be stored in the matrix (in fact it can't be).
  204.     // There is no need to preprocess it since its parameters are available
  205.     // in the 'translation' parameter (translation.x, translation.y);
  206.     //
  207.     // view a shear in direction d as a rotation over -d, a 'natural shear',
  208.     // and a rotation back over d.
  209.     // Let d be the shear angle and m its magnitude. The shear matrix can than
  210.     // be calculated as:
  211.     //
  212.     // ( cos( d) -sin( d) )   ( m  0 )   ( cos(-d) -sin(-d) )
  213.     // ( sin( d)  cos( d) )   ( 0 1/m)   ( sin(-d)  cos(-d) )
  214.     //
  215.     // Moreover we have sin(-x) = -sin( x), cos(-x) = cos( x)
  216.     // and we can define c := cos( d), s := sin( d) for brevity.
  217.     // We then find
  218.     //
  219.     // ( c -s )   ( m  0 )   ( c  s )
  220.     // ( s  c )   ( 0 1/m)   (-s  c )
  221.     //
  222.     // which (with c2 = c squared, etc) is equal to
  223.     //
  224.     // ( c2 m + s2/m    cs (m - 1/m) )
  225.     // ( cs (m - 1/m)   s2 m + c2/m  )
  226.     //
  227.     // These values will be put in the transformation matrix as follows:
  228.     //
  229.     // ( xform[ 0]  xform[ 1] )
  230.     // ( xform[ 2]  xform[ 3] )
  231.     //
  232.     // (well, almost in the transformation matrix; it appears to be simpler
  233.     // to use copies called xform_0, xform_1, xform_2, xform_3)
  234.     // Also, we do not store the transformation matrix, but we store the
  235.     // difference between the transformation matrix and the identity matrix.
  236.     //
  237.     // But first, we derive 'per frame' values for the flow parameters.
  238.     //
  239.     const double scaled_expansion       = scale( expansion, updaterate);
  240.     const double scaled_shear_magnitude = scale( shear_magnitude, updaterate);
  241.     const double scaled_rotation        = rotation / updaterate;
  242.     
  243.     const double cos_dir = cos( shear_direction degrees);
  244.     const double sin_dir = sin( shear_direction degrees);
  245.  
  246.     const double cos_squared = cos_dir * cos_dir;
  247.     const double sin_squared = sin_dir * sin_dir;
  248.     const double cos_sin     = cos_dir * sin_dir;
  249.  
  250.     const double m = scaled_shear_magnitude;
  251.     const double one_over_m = 1 / m;
  252.  
  253.     const double xform_0 = cos_squared * m + sin_squared / m;
  254.     const double xform_1 = cos_sin * (m - one_over_m);
  255.     const double xform_2 = xform_1;
  256.     const double xform_3 = sin_squared * m + cos_squared / m;
  257.     //
  258.     // now apply the rotation:
  259.     //
  260.     // the transformation matrix for this operation is
  261.     //
  262.     // ( cos( r)  -sin( r) )
  263.     // ( sin( r)   cos( r) )
  264.     //
  265.     const double cos_rot = cos( scaled_rotation degrees);
  266.     const double sin_rot = sin( scaled_rotation degrees);
  267.  
  268.     double xform[ 4];
  269.     xform[ 0] = cos_rot * xform_0 - sin_rot * xform_2;
  270.     xform[ 1] = cos_rot * xform_1 - sin_rot * xform_3;
  271.     xform[ 2] = sin_rot * xform_0 + cos_rot * xform_2;
  272.     xform[ 3] = sin_rot * xform_1 + cos_rot * xform_3;
  273.     //
  274.     // apply the expansion (unrolling the loop):
  275.     //
  276.     xform[ 0] *= scaled_expansion;
  277.     xform[ 1] *= scaled_expansion;
  278.     xform[ 2] *= scaled_expansion;
  279.     xform[ 3] *= scaled_expansion;
  280.  
  281.     x11 = (short)(long)((xform[ 0] - 1.0) * 0x8000);
  282.     x12 = (short)(long)(xform[ 1] * 0x8000);
  283.     x21 = (short)(long)(xform[ 2] * 0x8000);
  284.     x22 = (short)(long)((xform[ 3] - 1.0) * 0x8000);
  285.     
  286.     trans_x = (long)(translation_x * 0x10000 / updaterate);
  287.     trans_y = (long)(translation_y * 0x10000 / updaterate);
  288. }
  289.  
  290. void flowdots::setsettings( const flowsettings &thesettings)
  291. {
  292.     *((flowsettings *)this) = thesettings;
  293.     changed();
  294. }
  295.  
  296. void flowdots::setsettings( const flowparams &theparams)
  297. {
  298.     //
  299.     //    This safety check could be added, but this function is written to obtain speed,
  300.     //    so we leave it out for the moment.
  301.     //
  302.     //    if( how_often == 0.0)
  303.     //    {
  304.     //        DebugStr( "\pflowdots::setsettings( flowparams) called while how_often == 0.0");
  305.     //    }
  306.     //
  307.     *((flowparams *)this) = theparams;
  308. }
  309.